/*
 * @Author: NingJian
 * @Date: 2021-07-29 11:37:24
 * @LastEditTime: 2021-07-29 16:56:33
 * @LastEditors: NingJian
 * @Description: 
 * @FilePath: /opencv-as-pillow-resize/src/image.h
 * 
 */

#ifndef IMAGE_HEAD_H
#define IMAGE_HEAD_H

#include <opencv2/opencv.hpp>

#ifdef _WIN32

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>

#else
/* For System that are not Windows, we'll need to define these. */

#if SIZEOF_SHORT == 2
#define INT16 short
#elif SIZEOF_INT == 2
#define INT16 int
#else
#define INT16 short /* most things works just fine anyway... */
#endif

#if SIZEOF_SHORT == 4
#define INT32 short
#elif SIZEOF_INT == 4
#define INT32 int
#elif SIZEOF_LONG == 4
#define INT32 long
#else
#define INT32 int
#endif

#if SIZEOF_LONG == 8
#define INT64 long
#elif SIZEOF_LONG_LONG == 8
#define INT64 long
#endif

#define INT8 signed char
#define UINT8 unsigned char

#define UINT16 unsigned INT16
#define UINT32 unsigned INT32

#endif

/* assume IEEE; tweak if necessary (patches are welcome) */
#define FLOAT16 UINT16
#define FLOAT32 float
#define FLOAT64 double

#ifdef _MSC_VER
typedef signed __int64 int64_t;
#endif

#ifdef __GNUC__
#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
#endif


#ifdef WORDS_BIGENDIAN
#define MAKE_UINT32(u0, u1, u2, u3) \
    ((UINT32)(u3) | ((UINT32)(u2) << 8) | ((UINT32)(u1) << 16) | ((UINT32)(u0) << 24))
#define MASK_UINT32_CHANNEL_0 0xff000000
#define MASK_UINT32_CHANNEL_1 0x00ff0000
#define MASK_UINT32_CHANNEL_2 0x0000ff00
#define MASK_UINT32_CHANNEL_3 0x000000ff
#else
#define MAKE_UINT32(u0, u1, u2, u3) \
    ((UINT32)(u0) | ((UINT32)(u1) << 8) | ((UINT32)(u2) << 16) | ((UINT32)(u3) << 24))
#define MASK_UINT32_CHANNEL_0 0x000000ff
#define MASK_UINT32_CHANNEL_1 0x0000ff00
#define MASK_UINT32_CHANNEL_2 0x00ff0000
#define MASK_UINT32_CHANNEL_3 0xff000000
#endif

#define SHIFTFORDIV255(a) ((((a) >> 8) + a) >> 8)

/* like (a * b + 127) / 255), but much faster on most platforms */
#define MULDIV255(a, b, tmp) (tmp = (a) * (b) + 128, SHIFTFORDIV255(tmp))

#define DIV255(a, tmp) (tmp = (a) + 128, SHIFTFORDIV255(tmp))

#define BLEND(mask, in1, in2, tmp1) DIV255(in1 *(255 - mask) + in2 * mask, tmp1)

#define PREBLEND(mask, in1, in2, tmp1) (MULDIV255(in1, (255 - mask), tmp1) + in2)

#define CLIP8(v) ((v) <= 0 ? 0 : (v) < 256 ? (v) : 255)

/* This is to work around a bug in GCC prior 4.9 in 64 bit mode.
   GCC generates code with partial dependency which is 3 times slower.
   See: https://stackoverflow.com/a/26588074/253146 */
#if defined(__x86_64__) && defined(__SSE__) && !defined(__NO_INLINE__) && \
    !defined(__clang__) && defined(GCC_VERSION) && (GCC_VERSION < 40900)
static float __attribute__((always_inline)) inline _i2f(int v) {
    float x;
    __asm__("xorps %0, %0; cvtsi2ss %1, %0" : "=x"(x) : "r"(v));
    return x;
}
#else
static float inline _i2f(int v) { return (float)v; }
#endif


#define IMAGING_PIXEL_1(im, x, y) ((im)->image8[(y)][(x)])
#define IMAGING_PIXEL_L(im, x, y) ((im)->image8[(y)][(x)])
#define IMAGING_PIXEL_LA(im, x, y) ((im)->image[(y)][(x)*4])
#define IMAGING_PIXEL_P(im, x, y) ((im)->image8[(y)][(x)])
#define IMAGING_PIXEL_PA(im, x, y) ((im)->image[(y)][(x)*4])
#define IMAGING_PIXEL_I(im, x, y) ((im)->image32[(y)][(x)])
#define IMAGING_PIXEL_F(im, x, y) (((FLOAT32 *)(im)->image32[y])[x])
#define IMAGING_PIXEL_RGB(im, x, y) ((im)->image[(y)][(x)*4])
#define IMAGING_PIXEL_RGBA(im, x, y) ((im)->image[(y)][(x)*4])
#define IMAGING_PIXEL_CMYK(im, x, y) ((im)->image[(y)][(x)*4])
#define IMAGING_PIXEL_YCbCr(im, x, y) ((im)->image[(y)][(x)*4])

#define IMAGING_PIXEL_UINT8(im, x, y) ((im)->image8[(y)][(x)])
#define IMAGING_PIXEL_INT32(im, x, y) ((im)->image32[(y)][(x)])
#define IMAGING_PIXEL_FLOAT32(im, x, y) (((FLOAT32 *)(im)->image32[y])[x])


/* standard filters */
#define IMAGING_TRANSFORM_NEAREST 0
#define IMAGING_TRANSFORM_BOX 4
#define IMAGING_TRANSFORM_BILINEAR 2
#define IMAGING_TRANSFORM_HAMMING 5
#define IMAGING_TRANSFORM_BICUBIC 3
#define IMAGING_TRANSFORM_LANCZOS 1

#define IMAGING_PAGE_SIZE (4096)

/* pixel types */
#define IMAGING_TYPE_UINT8 0
#define IMAGING_TYPE_INT32 1
#define IMAGING_TYPE_FLOAT32 2
#define IMAGING_TYPE_SPECIAL 3 /* check mode for details */

#define IMAGING_MODE_LENGTH \
    6 + 1 /* Band names ("1", "L", "P", "RGB", "RGBA", "CMYK", "YCbCr", "BGR;xy") */


typedef struct ImagingMemoryInstance *Imaging;

typedef struct ImagingPaletteInstance *ImagingPalette;


typedef struct {
    char *ptr;
    int size;
} ImagingMemoryBlock;

struct ImagingPaletteInstance {
    /* Format */
    char mode[IMAGING_MODE_LENGTH]; /* Band names */

    /* Data */
    UINT8 palette[1024]; /* Palette data (same format as image data) */

    INT16 *cache;   /* Palette cache (used for predefined palettes) */
    int keep_cache; /* This palette will be reused; keep cache */
};

struct ImagingMemoryInstance {
    /* Format */
    char mode[IMAGING_MODE_LENGTH]; /* Band names ("1", "L", "P", "RGB", "RGBA", "CMYK",
                                       "YCbCr", "BGR;xy") */
    int type;                       /* Data type (IMAGING_TYPE_*) */
    int depth;                      /* Depth (ignored in this version) */
    int bands;                      /* Number of bands (1, 2, 3, or 4) */
    int xsize;                      /* Image dimension. */
    int ysize;

    /* Colour palette (for "P" images only) */
    ImagingPalette palette;

    /* Data pointers */
    UINT8 **image8;  /* Set for 8-bit images (pixelsize=1). */
    INT32 **image32; /* Set for 32-bit images (pixelsize=4). */

    /* Internals */
    char **image;               /* Actual raster data. */
    char *block;                /* Set if data is allocated in a single block. */
    ImagingMemoryBlock *blocks; /* Memory blocks for pixel storage */

    int pixelsize; /* Size of a pixel, in bytes (1, 2 or 4) */
    int linesize;  /* Size of a line, in bytes (xsize * pixelsize) */

    /* Virtual methods */
    void (*destroy)(Imaging im);
};


extern Imaging
ImagingResample(Imaging imIn, int xsize, int ysize, int filter, float box[4]);

extern Imaging
ImagingNewDirty(const char *mode, int xsize, int ysize);

extern void
ImagingDelete(Imaging im);

extern cv::Mat ImageResize_8UC3(const cv::Mat & src, int xsize, int ysize, int filter = IMAGING_TRANSFORM_BILINEAR);

#endif // IMAGE_HEAD

